﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TestXNA3.Actors;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;
using TestXNA3.Components;
using TestXNA3.Render;
using Microsoft.Xna.Framework.Graphics;
using XMas;

namespace TestXNA3.Games.TPGame
{
    public class Character : Actor
    {
        private StillDesign.PhysX.CapsuleController _capsuleController;
        private Camera _camera;
        private float _jump = 0.0f;

        private int _speed = 0;
        public int Speed
        {
            get { return _speed; }
            set { _speed = value; }
        }

        private SkeletalMeshComponent _skeletalMeshComponent;

        private Matrix _prevFaceMatrix = Matrix.Identity;

        public Character(Camera cameraActor)
        {
            _camera = cameraActor;
        }

        public Character()
        {
            _camera = null;
        }

        public void Init()
        {
            if (!GameStartup.InEditor)
            {
                StillDesign.PhysX.ControllerManager manager = Scene.PhysicsScene.CreateControllerManager();
                StillDesign.PhysX.CapsuleControllerDescription capsuleControllerDesc = new StillDesign.PhysX.CapsuleControllerDescription(25f, 50f);
                capsuleControllerDesc.SlopeLimit = (float)Math.Cos(MathHelper.ToRadians(45.0f));
                capsuleControllerDesc.StepOffset = 8f;
                capsuleControllerDesc.UpDirection = StillDesign.PhysX.Axis.Y;
                capsuleControllerDesc.ClimbingMode = StillDesign.PhysX.CapsuleClimbingMode.Easy;
                _capsuleController = manager.CreateController(capsuleControllerDesc) as StillDesign.PhysX.CapsuleController;
                _capsuleController.Actor.Name = "CapsuleController";
                _capsuleController.Position = _camera.Location;
            }

            Scale = new Vector3(0.5f, 0.5f, 0.5f);

            _skeletalMeshComponent = new SkeletalMeshComponent("trooper/trooper", "trooper_");
            _skeletalMeshComponent.Translation = new Vector3(0, -104f, 0);
            _skeletalMeshComponent.AnimTree.ReflexionObject = this;
            AttachComponent(_skeletalMeshComponent);
        }

        protected override void preUpdate(float dt)
        {
            base.preUpdate(dt);

            if (GameStartup.InEditor)
                return;

            // Update jump
            _jump -= dt * 55f;
            if (_jump < 0f)
                _jump = 0f;

            // update keyboard & mouse
            MouseState currentMouseState = Mouse.GetState();
            float x = currentMouseState.X;
            float y = currentMouseState.Y;
            int centerX = GameStartup.Graphics.PresentationParameters.BackBufferWidth / 2;
            int centerY = GameStartup.Graphics.PresentationParameters.BackBufferHeight / 2;
            x -= centerX;
            y -= centerY;
            _camera.Yaw += -(((float)(currentMouseState.X - centerX)) * 0.1f);
            _camera.Pitch += -(((float)(currentMouseState.Y - centerY)) * 0.1f);
            Mouse.SetPosition(centerX, centerY);

            // move controller
            Vector3 dir = Vector3.Zero;
            float moveSpeed = 250.0f;

            KeyboardState currentKeyboardState = Keyboard.GetState();
            if (currentKeyboardState.IsKeyDown(Keys.W))
            {
                dir += _camera.Forward;
            }
            if (currentKeyboardState.IsKeyDown(Keys.S))
            {
                dir -= _camera.Forward;
            }

            if (currentKeyboardState.IsKeyDown(Keys.A))
            {
                dir -= _camera.Right;
            }
            if (currentKeyboardState.IsKeyDown(Keys.D))
            {
                dir += _camera.Right;
            }
            // Project on xy plane
            dir.Y = 0.0f;
            if (dir.Length() > 0.1f)
            {
                dir.Normalize();

                Matrix faceMatrix = Matrix.CreateScale(Scale) * Matrix.CreateLookAt(Vector3.Zero, new Vector3(dir.X, 0, -dir.Z), Up) * Matrix.CreateTranslation(_capsuleController.Position);
                Axis = faceMatrix;
                _prevFaceMatrix = Axis;
            }
            else
            {
                Matrix faceMatrix = _prevFaceMatrix;//Matrix.CreateScale(Scale) * Matrix.CreateTranslation(_capsuleController.Position);
                Axis = faceMatrix;
            }

            // Update sprint
            if (currentKeyboardState.IsKeyDown(Keys.LeftShift))
            {
                dir *= 2f;
            }

            dir *= moveSpeed * dt;

            // Update anim tree values
            if (dir.Length() < 1.0f)
            {
                Speed = 0;
            }
            else
            {
                if (dir.Length() < moveSpeed * dt)
                {
                    Speed = 1;
                }
                else
                {
                    Speed = 2;
                }
            }

            // Check for jump
            if (currentKeyboardState.IsKeyDown(Keys.Space))
            {
                Jump();
            }

            // Add gravity
            if (!GameStartup.InEditor)
            {
                dir += Scene.PhysicsScene.Gravity * 0.1f + new Vector3(0, _jump, 0);

                _capsuleController.Move(dir);
            }

            Vector3 start = Location;
            Vector3 end = Location - _camera.Forward * 94f;

            float t = 99999999f;
            if (Scene.Trace(start, end, true, ref t))
            {
                 /*_camera.Location = MathHelper.Lerp(_camera.Location, Location - _camera.Forward * MathHelper.Clamp(t - 4.0f, 10f, 94f)
                    + _camera.Right * 12f, 0.99f);*/
            }

            _camera.Location = Location - _camera.Forward * 256f;
        }

        private void Jump()
        {
            if (_jump > 0f)
                return;

            Ray ray = new Ray(Location, -Up);
            //StillDesign.PhysX.RaycastHit hit = Scene.PhysicsScene.RaycastClosestShape(ray, StillDesign.PhysX.ShapesType.Dynamic);

            float lastDist = 999999f;
            Actor a = Scene.traceSelfAndChilds(Scene, ray.Position, ray.Position + ray.Direction * 256f, true, ref lastDist);
            if (a != null && lastDist < 256f)
            {
                _jump = 24f;
            }
        }
    }
}
